<?php
/*
 * 第三方支付 支付接口
 * Author ZJZ
 * Edit 2018.4.15
*/
!defined('DEBUG') AND exit('Access Denied.');
$i = param(1);
// 发起支付-选择通道-地址跳转中转
if ($i == 'pay') {
    if ($method != 'POST') exit('非法请求');
    $data = $_POST;
    //验证必填参数
    $res = verificationData($data);
    if ($res['status'] == false) {
        exit($res['msg']);
    }
    //根据接受的商户ID 查询出对应的配置
    $userData = db_find_one('user', ['apino' => $data['partner']]);
    if (empty($userData)) {
        exit('商户号错误');
    }

    // 获取到秘钥验证签名
    $data['key'] = $userData['apisecret'];
    if ($data['sign'] != sign($data)) {
        exit('签名错误！');
    }
    if ($userData['coin'] <= 0) {
        exit('商户余额不足,请充值!');
    }
    if (!isset($data['chname'])) {
        exit('缺少chname参数');
    }
    if ($data['chname'] != '' ) {//指定通道
        $channel = db_find_one('channel', ['name' => $data['chname'], 'uid' => $userData['uid']]);
        if (empty($channel)) {
            exit('通道不存在');
        } else {
            $data['chid'] = $channel['id'];
            $data['host'] = $channel['host'];
        }
    } else {//根据访客ip选择通道
        //      根据用户配置决定通道顺序
        $channel = db_find('channel', ['uid' => $userData['uid'], 'is_open' => 1]);
        if (empty($channel)) {
            exit('无有效通道');
        }
        switch ($userData['is_mode']) {
            case 1: // 随机
                $channel = $channel[array_rand($channel)];
                $data['chid'] = $channel['id'];
                $data['host'] = $channel['host'];
                break;
            case 2: // 顺序 TODO 顺序
                $redis = new cache_redis();
                $res =orderChannel($channel);
                $data['chid'] = $res['id'];
                $data['host'] = $res['host'];
                $redis->delete('channel' . $res['id']);
                break;
        }

    }//end 根据访客ip选择通道

    $channel = pay_channel_read($data['chid']);
    $paymoneyMax = verificationPayMaxMoney($data['chid']);
    if ($paymoneyMax >= $channel['max_price']) {
        exit('该通道今日收款已到最大上线，最大限额为' . $channel['max_price'] . '元');
    }

    if ($channel['max_limit'] < $data['paymoney']) exit('该通道单笔最大限额为' . $channel['max_limit'] . '元');
    $gateway = pay_gateway_read($channel['gate_id']);
    if (!$gateway) exit('通道配置错误');
    // 检测重复订单
    $reorder = db_find_one('order', array('order_merchant' => $data['ordernumber'], 'uid' => $userData['uid']));
    if (!empty($reorder)) {
        exit('重复订单');
    }

    // 保存订单
    $order_plat = date('YmdHis') . rand(10000, 99999);// 平台订单号
    $arr = array('chid' => $data['chid'], 'channel' => $channel['name'], 'order_merchant' => $data['ordernumber'], 'order_plat' => $order_plat, 'uid' => $userData['uid'], 'username' => $userData['username'], 'parent' => $userData['parent'], 'coin' => $data['paymoney'], 'callbackurl' => $data['callbackurl'], 'hrefbackurl' => $data['hrefbackurl'], 'subject' => $data['subject'], 'time' => $time);
    $orderid = order_insert($arr);
    if ($orderid === FALSE) {
        exit('保存订单失败');
    }
    // 检测订单连续失败次数
    if ($conf['channel_overtimes'] > 0 && $conf['order_overtime'] > 0) {
        $order_lian = db_sql_find("SELECT status FROM " . $db->tablepre . "order where chid=" . $data['chid'] . " and time>" . $channel['overtimes_starttime'] . " and time<" . ($time - $conf['order_overtime']) . " order by time desc limit " . $conf['channel_overtimes']);
        $order_lian_counts = count($order_lian);
        if ($order_lian_counts == $conf['channel_overtimes']) {//连续多少条
            $order_lian_status = 0;
            foreach ($order_lian as $ol) {
                if ($ol['status'] == 1) $order_lian_status = 1;
            }
            if ($order_lian_status == 0) {
                db_update('channel', array('id' => $data['chid']), array('is_open' => 0, 'close_reason' => 2));//关闭通道和原因
            }
        }
    }
    // 获取商品名称
    $alipay_subject = $data['subject'];//默认商品名称为订单标题
    $cid = $channel['cate'];
    if ($cid) {//设置了分类
        $cate_price = db_sql_find_one("SELECT id FROM " . $db->tablepre . "cate_price where   cid=" . $cid . " and price_start<=" . $data['paymoney'] . " and price_end>=" . $data['paymoney']);
        if (!empty($cate_price)) {//找到分类下的价格段
            $pid = $cate_price['id'];
            $cate_goods = db_sql_find_one("SELECT name FROM " . $db->tablepre . "cate_goods where cid=" . $cid . " and pid=" . $pid . " order by RAND()");//随机获取价格段下的商品名
            if (!empty($cate_goods)) {//找到商品名
                $alipay_subject = $cate_goods['name'];
            }
        }
    }
    if (empty($alipay_subject)) {//未设置商品名称时，用订单标题
        $alipay_subject = $data['subject'];
    }
    $gateway['shortname'] = 'ALIPAY';
    // 根据渠道不同发起不同的支付
    switch ($gateway['shortname']) {
        // 支付宝
        case "ALIPAY":
            $payName = '支付宝扫码支付';
            // 发起请求
            require_once XIUNOPHP_PATH . 'AlipayService.class.php';
            //请求参数
            $requestConfigs = array(
                'out_trade_no' => $order_plat, //你自己的商品订单号，不能重复
                'total_amount' => $data['paymoney'], //单位 元
                'subject' => $alipay_subject, //订单标题
                'timeout_express' => '5m' //该笔订单允许的最晚付款时间，逾期将关闭交易。取值范围：1m～15d。m-分钟，h-小时，d-天，1c-当天（1c-当天的情况下，无论交易何时创建，都在0点关闭）。 该参数数值不接受小数点， 如 1.5h，可转换为 90m。
            );
            // 异步通知地址
            $callback = 'http://' . $_SERVER['HTTP_HOST'] . '/' . url('pay-callback-alipay');//通知地址
            $commonConfigsMore = array(
                'method' => 'alipay.trade.precreate', //接口名称
                'notify_url' => $callback //付款成功后的异步回调地址（只有成功后才会调用）
            );
            $aliPay = new AlipayService($gateway['api_id'], $gateway['api_secret'], $gateway['api_publickey'], $gateway['url']);
            $result = $aliPay->doRequest($requestConfigs, $commonConfigsMore);
            //print_r($result);
            if ($result->code && $result->code == '10000') {
                //auto_transfer($chid);//自动转账
                $resPay = ['status' => 10000, 'msg' => '成功', 'url' => $result->qr_code];
                echo json_encode($resPay);
                exit();
            } else {
                exit($result->sub_msg);
            }
            break;
        default:
            exit('渠道错误');
    }


// 发起支付
} else if ($i == 'alipay') {


// 轮询订单实现异步/同步回调
} else if ($i == 'query') {
    // 获取参数
    $order = param('number');//平台订单号
    // 验证参数
    $order = order_read_by_platnumber($order);
    if (!$order) exit('无效订单');
    $merchant = user_read($order['uid']);
    if (!$merchant) exit('商家错误');
    $chid = $order['chid'];
    $channel = pay_channel_read($chid);
    if (!$chid) exit('通道错误');
    $gateway = pay_gateway_read($channel['gate_id']);
    if (!$gateway) exit('网关错误');
    // 订单已经成功支付
    if ($order['status'] == 1) {
        callback(1);
    }
    //API查询订单状态
    // 发起请求
    require_once XIUNOPHP_PATH . 'AlipayService.class.php';
    //请求参数
    $requestConfigs = array(
        'out_trade_no' => $order['order_plat'], //平台订单号
    );
    //公共参数
    $commonConfigsMore = array(
        'method' => 'alipay.trade.query'  //接口名称
    );
    $aliPay = new AlipayService($gateway['api_id'], $gateway['api_secret'], $gateway['api_publickey'], $gateway['url']);
    $result = $aliPay->doRequest($requestConfigs, $commonConfigsMore);
    //print_r($result);
    if ($result->code != '10000') {
        message(0, '未支付');//继续查询
    }
    if ($result->out_trade_no != $order['order_plat']) {//查询到的平台交易单号和提交的不一致
        message(-1, '交易单号和提交的不一致');//终断轮询
    }
    if ($result->trade_status == 'TRADE_CLOSED' || $result->trade_status == 'TRADE_FINISHED') {//未付款交易超时关闭，或支付完成后全额退款 || 交易结束，不可退款
        message(-1, '交易关闭');//终断轮询
    }

    if ($result->trade_status == 'TRADE_SUCCESS') {//交易支付成功
        $orderstatus = 1;
        // 通知
        callback($orderstatus);
    }
    message(0, '未支付');//继续查询

// 异步回调
} else if ($i == 'callback') {
    $type = param(2);
    // 获取参数
    switch ($type) {
        case 'alipay':
            $ordernumber = param('out_trade_no');
            $orderstatus = param('trade_status') == 'TRADE_SUCCESS' ? 1 : 0;
            $paymoney = param('total_amount');
            break;
        default:
            exit('回调渠道错误');
            break;
    }
    // 验证参数
    $order = order_read_by_platnumber($ordernumber);
    if (!$order) exit('无效订单');
    if ($order['coin'] != $paymoney) exit('金额错误');
    $merchant = user_read($order['uid']);
    if (!$merchant) exit('商家错误');
    $chid = $order['chid'];
    $channel = pay_channel_read($chid);
    if (!$chid) exit('通道错误');
    $gateway = pay_gateway_read($channel['gate_id']);
    if (!$gateway) exit('网关错误');
    // 验证签名
    require_once XIUNOPHP_PATH . 'AlipayService.class.php';
    $aliPay = new AlipayService($gateway['api_id'], $gateway['api_secret'], $gateway['api_publickey'], $gateway['url']);
    $result = $aliPay->rsaCheck($_POST);
    if (!$result) exit('签名错误');
    if ($orderstatus == 1) {
        if ($order['status'] != 1) {
            // 更新商户信息
            $coin_pay = $orderstatus == 1 ? $paymoney : 0;
            $rate_merchant = $merchant['rate']; //商户的费率
            //商户预存余额扣除
            $coin_merchant = round($coin_pay * $rate_merchant, 2);
            user__update($order['uid'], array('coin-' => $coin_merchant, 'tj_pay+' => $paymoney));
            // 代理奖励
            $coin_proxy = 0;
            $rate_proxy = 0;
            if ($merchant['parent'] != 0) { //该商户有上级
                $proxy = user_read($merchant['parent']);
                $rate_proxy = $proxy['rate'];//代理费率
                $coin_proxy = $paymoney * ($rate_merchant - $rate_proxy);
                if ($coin_proxy < 0) $coin_proxy = -$coin_proxy;
                user__update($merchant['parent'], array('coin+' => $coin_proxy));
            }
            // 更新订单
            $coin_plat = $paymoney * $rate_proxy;
            order_update($order['id'], array('status' => $orderstatus, 'coin_pay' => $coin_pay, 'coin_merchant' => $paymoney, 'coin_proxy' => $coin_proxy, 'coin_plat' => $coin_plat));
            //更新通道的累积收款金额和实时剩余金额
            $money = $paymoney * 0.99;
            db_update('channel', array('id' => $chid), array('money_total+' => $paymoney, 'money+' => $money));
        }
        // 通知
        $subject = $order['subject'];
        $noticeData = [
            'partner' => $merchant['apino'],
            'ordernumber' => $order['order_merchant'],
            'orderstatus' => $orderstatus,
            'paymoney' => $paymoney,
            'subject' => $subject,
            'sysnumber' => $order['order_plat'],
            'key' => $merchant['apisecret']
        ];
        $noticeData['sign'] = sign($noticeData);
        unset($noticeData['key']);
        $notice_url = $order['callbackurl'] . (strpos($order['callbackurl'], '?') === false ? '?' : '&') . http_build_query($noticeData);
        order_update($order['id'], ['notice_data' => $notice_url]);
        $res = @file_get_contents($notice_url);

        if (trim($res) == 'ok') {
            order_update($order['id'], array('notice_status' => 1));//标记为已异步通知
            exit('success');//终止alipay推算异步通知
        }
    }
}


//异步/同步回调
function callback($orderstatus)
{
    global $order, $merchant;
    $sign = md5('partner=' . $merchant['apino'] . '&ordernumber=' . $order['order_merchant'] . '&orderstatus=' . $orderstatus . '&paymoney=' . $order['coin'] . '&subject=' . $order['subject'] . $merchant['apisecret']);//签名
    $param = 'partner=' . $merchant['apino'] . '&ordernumber=' . $order['order_merchant'] . '&orderstatus=' . $orderstatus . '&paymoney=' . $order['coin'] . '&subject=' . $order['subject'] . '&sysnumber=' . $order['order_plat'] . '&sign=' . $sign;//参数
    $res = @file_get_contents($order['callbackurl'] . (strpos($order['callbackurl'], '?') === false ? '?' : '&') . $param);//异步通知
    if (trim($res) == 'ok') order_update($order['id'], array('notice_status' => 1));//标记为已异步通知
    $hrefbackurl = '';
    if ($order['hrefbackurl'] != '') $hrefbackurl = $order['hrefbackurl'] . (strpos($order['hrefbackurl'], '?') === false ? '?' : '&') . $param;
    message($orderstatus, $hrefbackurl);//同步返回
    exit;
}

//是否移动端
function ismobile()
{
    // 如果有HTTP_X_WAP_PROFILE则一定是移动设备
    if (isset($_SERVER['HTTP_X_WAP_PROFILE'])) {
        return true;
    }
    // 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
    if (isset($_SERVER['HTTP_VIA'])) {
        // 找不到为flase,否则为true
        return stristr($_SERVER['HTTP_VIA'], "wap") ? true : false;
    }
    // 脑残法，判断手机发送的客户端标志,兼容性有待提高
    if (isset($_SERVER['HTTP_USER_AGENT'])) {
        $clientkeywords = array('nokia', 'sony', 'ericsson', 'mot', 'samsung', 'htc', 'sgh', 'lg', 'sharp', 'sie-', 'philips', 'panasonic', 'alcatel', 'lenovo', 'iphone', 'ipod', 'blackberry', 'meizu', 'android', 'netfront', 'symbian', 'ucweb', 'windowsce', 'palm', 'operamini', 'operamobi', 'openwave', 'nexusone', 'cldc', 'midp', 'wap', 'mobile');
        // 从HTTP_USER_AGENT中查找手机浏览器的关键字
        if (preg_match("/(" . implode('|', $clientkeywords) . ")/i", strtolower($_SERVER['HTTP_USER_AGENT']))) {
            return true;
        }
    }
    // 协议法，因为有可能不准确，放到最后判断
    if (isset($_SERVER['HTTP_ACCEPT'])) {
        // 如果只支持wml并且不支持html那一定是移动设备
        // 如果支持wml和html但是wml在html之前则是移动设备
        if ((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html')))) {
            return true;
        }
    }
    return false;
}

function auto_transfer($chid)
{
    global $aliPay, $time;
    $channel = pay_channel_read($chid);//重新获取
    if ($channel['money'] >= $channel['money_level']) {//到达转账触发金额，启动转账
        $amount = $channel['money_trans'];
        // 保存转账记录
        $order_site = date('YmdHis') . rand(10000, 99999);// 商户转账订单号
        $arr = array('account' => $channel['transto'], 'chid' => $channel['id'], 'channel' => $channel['name'], 'order_site' => $order_site, 'order_alipay' => '', 'money' => $amount, 'status' => 0, 'dotype' => 0, 'time' => $time);
        $transid = db_insert('transfer', $arr);
        // 发起请求
        //请求参数
        $requestConfigs = array(
            'out_biz_no' => $order_site, //你自己的转账唯一订单号
            'payee_type' => 'ALIPAY_LOGONID', //收款方账户类型 1、ALIPAY_USERID：支付宝账号对应的支付宝唯一用户号。以2088开头的16位纯数字组成。  2、ALIPAY_LOGONID：支付宝登录号，支持邮箱和手机号格式。
            'payee_account' => $channel['transto'],  //收款方账户
            'amount' => $amount  //转账金额，单位：元 只支持2位小数，金额必须大于等于0.1元
        );
        //公共参数
        $commonConfigsMore = array(
            'method' => 'alipay.fund.trans.toaccount.transfer'  //接口名称
        );
        $result = $aliPay->doRequest($requestConfigs, $commonConfigsMore);
        //print_r($result);exit;
        if ($result->code && $result->code == '10000') {
            db_update('transfer', array('id' => $transid), array('status' => 1, 'order_alipay' => $result->order_id));//更新订单状态和支付宝转账单号
            db_update('channel', array('id' => $channel['id']), array('money-' => $amount));//更新通道的实时剩余金额
        } else {
            db_update('transfer', array('id' => $transid), array('fail_msg' => $result->sub_msg));//存入失败原因
            if ($result->sub_code != 'SYSTEM_ERROR' && $result->sub_code != 'PAYER_BALANCE_NOT_ENOUGH') {//系统繁忙和余额不足 不关闭通道
                db_update('channel', array('id' => $channel['id']), array('is_open' => 0, 'close_reason' => 1));//关闭通道和原因
                db_update('gateway', array('id' => $channel['gate_id']), array('status' => 1));//标记网关异常状态
            }
        }
    }
}

//根据IP找地区
function get_area($ip)
{
    $res = http_get('http://opendata.baidu.com/api.php?query=' . $ip . '&co=&resource_id=6006&t=1412300361645&ie=utf8&oe=utf8&cb=op_aladdin_callback&format=json&tn=baidu&cb=jQuery1102026811896078288555_1412299994977&_=1412299994981');
    if ($res) {
        $out = array();
        preg_match('#\"location\"\:\"(.+)\"#U', $res, $out);
        if (!empty($out) && isset($out[1])) {
            return $out[1];
        } else {
            return '';
        }
    }
    return '';
}

// 严重必填参数
function verificationData($data)
{

    $arr = ['partner', 'banktype', 'ordernumber', 'paymoney', 'subject', 'callbackurl', 'hrefbackurl', 'sign'];
    foreach ($arr as $v) {
        if (!isset($data[$v]) || empty($data[$v])) {
            return ['status' => false, 'msg' => $v . ' 参数错误'];
        }
    }
    return ['status' => true];
}


// 签名函数
function sign($data)
{
    ksort($data);
    $sign = [];
    foreach ($data as $k => $v) {
        if ($k != 'sign') {
            $sign[] = $k . '=' . $v;
        }
    }
    return md5(implode('&', $sign));
}

/**
 * @return int
 * * @param $channel int 通道id
 * 验证通道总金额
 */
function verificationPayMaxMoney($channel)
{
    $startTime = strtotime(date('Ymd00:00:00'));
    $ensTime = strtotime(date('Ymd23:59:59'));
    $order = db_find('order', ['chid' => $channel, 'time' => ['>=' => $startTime, '<=' => $ensTime]]);
    $money = 0;
    foreach ($order as $v) {
        if ($v['status'] == 1) {
            $money += $v['coin_pay'];
        }
    }
    return $money;
}

function orderChannel($channel)
{
    $redis = new cache_redis();
    foreach ($channel as $v) {
        $res =$redis->get('channel' . $v['id']);
        if ($res) {
            return $v;
        }
    }
    $channels = 0;
    foreach ($channel as $v) {
        $redis->set('channel' . $v['id'],$v['id']);
        $channels = $v;
    }
    return $channels;
}


?>